package org.hxyjs.shiro;

import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.authc.ExcessiveAttemptsException;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.cache.Cache;
import org.apache.shiro.cache.CacheManager;
import org.apache.shiro.crypto.SecureRandomNumberGenerator;
import org.apache.shiro.crypto.hash.SimpleHash;

import java.util.concurrent.atomic.AtomicInteger;

public class RetryLimitCredentialsMatcher extends HashedCredentialsMatcher {

	// 集群中可能会导致出现验证多过5次的现象，因为AtomicInteger只能保证单节点并发
	private Cache<String, AtomicInteger> passwordRetryCache;
	int maxRetryNum = 5;

	public RetryLimitCredentialsMatcher(CacheManager cacheManager) {
		passwordRetryCache = cacheManager.getCache("passwordRetryCache");
	}
//匹配密码的过程，尝试一次就原子类计数加1，无论成功与否都加1 只不过成功了 计数会清零
	@Override
	public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) {
		String username = (String) token.getPrincipal();

		AtomicInteger retryCount = passwordRetryCache.get(username);
		if (null == retryCount) {
			retryCount = new AtomicInteger(0);
			passwordRetryCache.put(username, retryCount);
		}
		System.out.println(maxRetryNum+"   sssssssssssssssssssssssss");
		// retry count + 1  无论成功与否都先加1
		if (retryCount.incrementAndGet() > maxRetryNum) {
			// logger.warn("username: " + username + " tried to login more than
			// 5 times in period");
			throw new ExcessiveAttemptsException(
					"username: " + username + " tried to login more than 5 times in period");
		}
		//如果一次匹配成功 之前的错误次数 直接归零
		boolean matches = super.doCredentialsMatch(token, info);
		if (matches) {
			// clear retry data
			passwordRetryCache.remove(username);
		}
		return matches;
	}

	// 测试
	public static void main(String[] args) {
		String algorithmName = "md5";
		String username = "金湖餐厅";
		String password = "123456";
		String salt1 = username;
		String salt2 = new SecureRandomNumberGenerator().nextBytes().toHex();
		int hashIterations = 3;
		SimpleHash hash = new SimpleHash(algorithmName, password, salt1 + salt2, hashIterations);//参数1选择md5加密方式，参数2 原始密码  参数3 一个特定值（salt1 用户名 ）
		// +salt 2随机值 作为加工的佐料，参数4 佐料加的次数
		String encodedPassword = hash.toHex();//hash出来的是128位的2进制方便存储转换为32为的字符串
		System.out.println(encodedPassword);//加密后的密码
		System.out.println(salt2);//随机值
	}

}
